<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>代码生成器</title>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/vs2015.min.css">
    <style>
        body {
            margin: 0;
            padding: 0;
            background-color: #f5f7fa;
        }
        .app-container {
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }
        .el-header {
            background-color: #409eff;
            color: white;
            line-height: 60px;
            padding: 0 20px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
        }
        .el-header h2 {
            margin: 0;
        }
        .el-main {
            padding: 20px;
            background-color: #fff;
            border-radius: 4px;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
            margin: 20px;
            flex: 1;
        }
        .button-group {
            margin-top: 20px;
        }
        .button-group .el-button {
            margin-right: 10px;
        }
        .preview-dialog {
            width: 90%;
            margin: 0 auto;
        }
        .preview-dialog .el-dialog {
            margin-top: 3vh !important;
            margin-bottom: 3vh;
            height: 94vh;
            display: flex;
            flex-direction: column;
        }
        .preview-dialog .el-dialog__body {
            flex: 1;
            overflow: hidden;
            display: flex;
            padding: 10px;
            background-color: #1e1e1e;
        }
        .file-tree {
            width: 280px;
            border-right: 1px solid #333;
            overflow-y: auto;
            padding: 10px;
            background-color: #252526;
            color: #d4d4d4;
        }
        .code-content {
            flex: 1;
            margin-left: 10px;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            background-color: #1e1e1e;
        }
        .code-header {
            padding: 10px;
            border-bottom: 1px solid #333;
            color: #d4d4d4;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .code-preview {
            flex: 1;
            overflow-y: auto;
            padding: 10px;
            margin: 0;
            font-family: 'Consolas', 'Monaco', monospace;
            font-size: 14px;
            line-height: 1.5;
            background-color: #1e1e1e;
            color: #d4d4d4;
        }
        .el-tree {
            background-color: transparent;
            color: #d4d4d4;
        }
        .el-tree-node__content {
            height: 32px;
        }
        .el-tree-node__content:hover {
            background-color: #37373d;
        }
        .el-tree-node.is-current > .el-tree-node__content {
            background-color: #37373d !important;
            color: #fff;
        }
        .file-icon {
            margin-right: 5px;
            font-size: 16px;
        }
        .folder-icon {
            color: #dcb67a;
        }
        .file-icon-java {
            color: #b07219;
        }
        .file-icon-xml {
            color: #e34c26;
        }
        .preview-dialog .el-dialog__header {
            background-color: #252526;
            padding: 15px 20px;
            margin: 0;
            border-bottom: 1px solid #333;
        }
        .preview-dialog .el-dialog__title {
            color: #d4d4d4;
        }
        .preview-dialog .el-dialog__headerbtn .el-dialog__close {
            color: #d4d4d4;
        }
        .preview-dialog .el-dialog__footer {
            background-color: #252526;
            border-top: 1px solid #333;
            padding: 15px 20px;
        }
        .el-form-item {
            margin-bottom: 22px;
        }
        .el-form-item__label {
            font-weight: 500;
        }
        .el-input__inner {
            border-radius: 4px;
        }
        .el-select {
            width: 100%;
        }
    </style>
</head>
<body>
    <div id="app" class="app-container">
        <el-container>
            <el-header>
                <h2>MyBatis代码生成器</h2>
            </el-header>
            <el-main>
                <el-form ref="form" :model="form" label-width="120px">
                    <el-form-item label="选择数据表">
                        <el-select v-model="form.tableName"
                                 placeholder="请选择数据表"
                                 v-if="!isMultiSelect"
                                 filterable
                                 :filter-method="filterTables"
                                 :loading="loading">
                            <el-option
                                v-for="table in filteredTables"
                                :key="table.tableName"
                                :label="table.tableName + (table.tableComment ? ' (' + table.tableComment + ')' : '')"
                                :value="table.tableName">
                            </el-option>
                        </el-select>
                        <el-select v-model="form.selectedTables"
                                 multiple
                                 collapse-tags
                                 placeholder="请选择数据表"
                                 v-if="isMultiSelect"
                                 filterable
                                 :filter-method="filterTables"
                                 :loading="loading">
                            <el-option
                                v-for="table in filteredTables"
                                :key="table.tableName"
                                :label="table.tableName + (table.tableComment ? ' (' + table.tableComment + ')' : '')"
                                :value="table.tableName">
                                <span>{{ table.tableName }}</span>
                                <span v-if="table.tableComment" style="float: right; color: #8492a6; font-size: 13px">
                                    {{ table.tableComment }}
                                </span>
                            </el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item>
                        <el-switch
                            v-model="isMultiSelect"
                            active-text="多选模式"
                            inactive-text="单选模式">
                        </el-switch>
                    </el-form-item>
                    <el-form-item label="包名">
                        <el-input v-model="form.basePackage" placeholder="例如：com.example"></el-input>
                    </el-form-item>
                    <el-form-item label="作者">
                        <el-input v-model="form.author"></el-input>
                    </el-form-item>
                    <el-form-item>
                        <el-switch
                            v-model="form.lombokEnabled"
                            active-text="启用Lombok"
                            inactive-text="不启用Lombok">
                        </el-switch>
                    </el-form-item>
                    <el-form-item>
                        <el-switch
                            v-model="form.swagger"
                            active-text="启用Swagger"
                            inactive-text="不启用Swagger">
                        </el-switch>
                    </el-form-item>
                    <el-form-item>
                        <el-switch
                            v-model="form.generateTest"
                            active-text="生成单元测试"
                            inactive-text="不生成单元测试">
                        </el-switch>
                    </el-form-item>
                    <el-form-item class="button-group">
                        <el-button type="primary" @click="previewCode" :loading="previewing" v-if="!isMultiSelect">预览代码</el-button>
                        <el-button type="primary" @click="generateCode" :loading="generating" v-if="!isMultiSelect">下载代码</el-button>
                        <el-button type="warning" @click="previewSelectedTables" :loading="previewing" v-if="isMultiSelect">预览代码</el-button>
                        <el-button type="warning" @click="generateSelectedTables" :loading="generating" v-if="isMultiSelect">下载代码</el-button>
                        <el-button type="success" @click="generateAllCode" :loading="generatingAll">生成所有表代码</el-button>
                    </el-form-item>
                </el-form>

                <!-- 预览对话框 -->
                <el-dialog
                    title="代码预览"
                    :visible.sync="previewVisible"
                    custom-class="preview-dialog"
                    :close-on-click-modal="false"
                    :close-on-press-escape="false">
                    <div class="file-tree">
                        <el-tree
                            :data="fileTreeData"
                            :props="defaultProps"
                            @node-click="handleNodeClick"
                            :highlight-current="true"
                            :default-expand-all="true"
                            node-key="id">
                            <span class="custom-tree-node" slot-scope="{ node, data }">
                                <span>
                                    <i :class="getFileIcon(data)"></i>
                                    {{ node.label }}
                                </span>
                            </span>
                        </el-tree>
                    </div>
                    <div class="code-content">
                        <div class="code-header">
                            <span>{{ currentFileName }}</span>
                            <el-button type="primary" size="small" @click="downloadCurrentFile" v-if="currentFileContent">
                                下载当前文件
                            </el-button>
                        </div>
                        <pre class="code-preview"><code :class="currentFileLanguage">{{ currentFileContent }}</code></pre>
                    </div>
                    <span slot="footer" class="dialog-footer">
                        <el-button type="primary" @click="downloadAllFiles">下载全部文件</el-button>
                        <el-button @click="previewVisible = false">关闭</el-button>
                    </span>
                </el-dialog>
            </el-main>
        </el-container>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {
                tables: [],
                filteredTables: [],
                generating: false,
                generatingAll: false,
                isMultiSelect: false,
                loading: false,
                searchTimeout: null,
                editor: null,
                saving: false,
                    form: {
                        tableName: '',
                        selectedTables: [],
                        basePackage: 'com.example',
                        author: 'Generator',
                        lombokEnabled: false,
                        swagger: false,
                        generateTest: true,
                        preserveStructure: true
                    },
                previewVisible: false,
                previewFiles: {},
                currentFile: '',
                currentFileName: '',
                currentFileContent: '',
                previewing: false,
                defaultProps: {
                    children: 'children',
                    label: 'label'
                },
                fileTreeData: []
            },
            created() {
                this.loadTables();
                this.initMonacoEditor();
            },
            mounted() {
                window.addEventListener('resize', this.handleResize);
            },
            beforeDestroy() {
                window.removeEventListener('resize', this.handleResize);
                if (this.editor) {
                    this.editor.dispose();
                }
            },
            methods: {
                initMonacoEditor() {
                    require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs' }});
                    require(['vs/editor/editor.main'], () => {
                        this.editor = monaco.editor.create(document.getElementById('monaco-editor'), {
                            value: '',
                            language: 'java',
                            theme: 'vs-dark',
                            automaticLayout: true,
                            minimap: {
                                enabled: true
                            },
                            scrollBeyondLastLine: false,
                            fontSize: 14,
                            tabSize: 4
                        });
                    });
                },
                handleResize() {
                    if (this.editor) {
                        this.editor.layout();
                    }
                },
                selectFile(tableName, fileName, content) {
                    this.currentFile = tableName ? tableName + '_' + fileName : fileName;
                    this.currentFileName = fileName;
                    this.currentFileContent = content;

                    if (this.editor) {
                        const language = fileName.endsWith('.java') ? 'java' :
                                      fileName.endsWith('.xml') ? 'xml' : 'plaintext';
                        monaco.editor.setModelLanguage(this.editor.getModel(), language);
                        this.editor.setValue(content);
                    }
                },
                async saveCurrentFile() {
                    if (!this.editor) return;

                    this.saving = true;
                    try {
                        const content = this.editor.getValue();
                        await axios.post('/api/generator/save', {
                            tableName: this.currentFile.split('_')[0],
                            fileName: this.currentFileName,
                            content: content
                        });
                        this.$message.success('保存成功');
                    } catch (error) {
                        this.$message.error('保存失败');
                        console.error('Error:', error);
                    } finally {
                        this.saving = false;
                    }
                },
                loadTables() {
                    this.loading = true;
                    axios.get('/api/generator/tables')
                        .then(response => {
                            this.tables = response.data;
                            this.filteredTables = response.data;
                        })
                        .catch(error => {
                            this.$message.error('获取表信息失败');
                        })
                        .finally(() => {
                            this.loading = false;
                        });
                },
                generateCode() {
                    if (!this.form.tableName) {
                        this.$message.warning('请选择数据表');
                        return;
                    }

                    this.generating = true;
                    axios({
                        method: 'post',
                        url: '/api/generator/generate',
                            params: {
                                ...this.form,
                                preserveStructure: this.form.preserveStructure,
                                generateTest: this.form.generateTest
                            },
                        responseType: 'blob'
                    })
                    .then(response => {
                        const contentDisposition = response.headers['content-disposition'];
                        let fileName = this.form.tableName + '_code.zip';
                        if (contentDisposition) {
                            const matches = contentDisposition.match(/filename=(.+)/);
                            if (matches && matches.length > 1) {
                                fileName = matches[1].replace(/"/g, '');
                            }
                        }
                        this.downloadFile(response.data, fileName);
                        this.$message.success('代码生成成功');
                    })
                    .catch(error => {
                        this.$message.error('代码生成失败');
                        console.error('Error:', error);
                    })
                    .finally(() => {
                        this.generating = false;
                    });
                },
                generateSelectedTables() {
                    if (!this.form.selectedTables || this.form.selectedTables.length === 0) {
                        this.$message.warning('请选择至少一个数据表');
                        return;
                    }

                    this.generating = true;
                    axios({
                        method: 'post',
                        url: '/api/generator/generate/selected',
                        params: {
                                tables: this.form.selectedTables.map(table => encodeURIComponent(table)).join(','),
                                author: encodeURIComponent(this.form.author),
                                basePackage: encodeURIComponent(this.form.basePackage),
                                preserveStructure: this.form.preserveStructure,
                                generateTest: this.form.generateTest
                        },
                        responseType: 'blob'
                    })
                    .then(response => {
                        const contentDisposition = response.headers['content-disposition'];
                        let fileName = 'selected_tables_code.zip';
                        if (contentDisposition) {
                            const matches = contentDisposition.match(/filename=(.+)/);
                            if (matches && matches.length > 1) {
                                fileName = matches[1].replace(/"/g, '');
                            }
                        }
                        this.downloadFile(response.data, fileName);
                        this.$message.success('选中表的代码生成成功');
                    })
                    .catch(error => {
                        this.$message.error('生成选中表代码失败');
                        console.error('Error:', error);
                    })
                    .finally(() => {
                        this.generating = false;
                    });
                },
                generateAllCode() {
                    this.$confirm('确定要生成所有表的代码吗？', '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        this.generatingAll = true;
                        axios({
                            method: 'post',
                            url: '/api/generator/generate/all',
                            params: {
                                author: this.form.author,
                                basePackage: this.form.basePackage,
                                preserveStructure: this.form.preserveStructure,
                                generateTest: this.form.generateTest
                            },
                            responseType: 'blob'
                        })
                        .then(response => {
                            const contentDisposition = response.headers['content-disposition'];
                            let fileName = 'all_tables_code.zip';
                            if (contentDisposition) {
                                const matches = contentDisposition.match(/filename=(.+)/);
                                if (matches && matches.length > 1) {
                                    fileName = matches[1].replace(/"/g, '');
                                }
                            }
                            this.downloadFile(response.data, fileName);
                            this.$message.success('所有表的代码生成成功');
                        })
                        .catch(error => {
                            this.$message.error('生成所有表代码失败');
                            console.error('Error:', error);
                        })
                        .finally(() => {
                            this.generatingAll = false;
                        });
                    }).catch(() => {
                        this.$message.info('已取消生成');
                    });
                },
                downloadFile(data, fileName) {
                    if (!(data instanceof Blob)) {
                        this.$message.error('下载文件格式错误');
                        return;
                    }
                    const blob = new Blob([data], { type: 'application/zip' });
                    const link = document.createElement('a');
                    link.href = window.URL.createObjectURL(blob);
                    link.download = fileName;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    window.URL.revokeObjectURL(link.href);
                },
                filterTables(value) {
                    if (this.searchTimeout) {
                        clearTimeout(this.searchTimeout);
                    }

                    this.searchTimeout = setTimeout(() => {
                        if (!value) {
                            this.filteredTables = this.tables;
                            return;
                        }

                        const searchValue = value.toLowerCase();
                        this.filteredTables = this.tables.filter(table => {
                            const tableName = table.tableName.toLowerCase();
                            const tableComment = (table.tableComment || '').toLowerCase();
                            return tableName.includes(searchValue) ||
                                   tableComment.includes(searchValue);
                        });
                    }, 200);
                },
                getFileIcon(data) {
                    if (data.type === 'folder') {
                        return 'el-icon-folder folder-icon';
                    }
                    if (data.label.endsWith('.java')) {
                        return 'el-icon-document file-icon-java';
                    }
                    if (data.label.endsWith('.xml')) {
                        return 'el-icon-document file-icon-xml';
                    }
                    return 'el-icon-document';
                },
                handleNodeClick(data) {
                    if (data.type === 'file') {
                        this.selectFile(data.tableName, data.label, data.content);
                    }
                },
                convertToTreeData(files, isMultiSelect = false) {
                    const createTreeNode = (path, isFile, content = null, tableName = null) => {
                        const parts = path.split('/');
                        const label = parts[parts.length - 1];
                        return {
                            id: path,
                            label: label,
                            type: isFile ? 'file' : 'folder',
                            content: content,
                            tableName: tableName,
                            children: isFile ? null : []
                        };
                    };

                    const insertIntoTree = (tree, path, content, tableName = null) => {
                        const parts = path.split('/');
                        let currentLevel = tree;

                        for (let i = 0; i < parts.length - 1; i++) {
                            const part = parts.slice(0, i + 1).join('/');
                            let folder = currentLevel.find(node => node.id === part);

                            if (!folder) {
                                folder = createTreeNode(part, false);
                                currentLevel.push(folder);
                            }
                            currentLevel = folder.children;
                        }

                        const filePath = parts.join('/');
                        currentLevel.push(createTreeNode(filePath, true, content, tableName));
                    };

                    const tree = [];

                    if (isMultiSelect) {
                        Object.entries(files).forEach(([tableName, tableFiles]) => {
                            Object.entries(tableFiles).forEach(([fileName, content]) => {
                                insertIntoTree(tree, `${tableName}/${fileName}`, content, tableName);
                            });
                        });
                    } else {
                        Object.entries(files).forEach(([fileName, content]) => {
                            insertIntoTree(tree, fileName, content);
                        });
                    }

                    return tree;
                },
                async previewCode() {
                    if (!this.form.tableName) {
                        this.$message.warning('请选择数据表');
                        return;
                    }

                    this.previewing = true;
                    try {
                        const response = await axios.post('/api/generator/preview', null, {
                            params: {
                                tableName: this.form.tableName,
                                author: this.form.author,
                                basePackage: this.form.basePackage
                            }
                        });
                        this.previewFiles = response.data;
                        this.fileTreeData = this.convertToTreeData(response.data);
                        this.previewVisible = true;

                        // 默认选择第一个文件
                        if (this.fileTreeData.length > 0) {
                            const firstFile = this.fileTreeData[0];
                            this.handleNodeClick(firstFile);
                        }
                    } catch (error) {
                        this.$message.error('代码预览失败');
                        console.error('Error:', error);
                    } finally {
                        this.previewing = false;
                    }
                },
                async previewSelectedTables() {
                    if (!this.form.selectedTables || this.form.selectedTables.length === 0) {
                        this.$message.warning('请选择至少一个数据表');
                        return;
                    }

                    this.previewing = true;
                    try {
                        const response = await axios.post('/api/generator/preview/selected', null, {
                            params: {
                                tables: this.form.selectedTables.join(','),
                                author: this.form.author,
                                basePackage: this.form.basePackage
                            }
                        });
                        this.previewFiles = response.data;
                        this.fileTreeData = this.convertToTreeData(response.data, true);
                        this.previewVisible = true;

                        // 默认选择第一个文件
                        if (this.fileTreeData.length > 0) {
                            const firstTable = this.fileTreeData[0];
                            if (firstTable.children && firstTable.children.length > 0) {
                                this.handleNodeClick(firstTable.children[0]);
                            }
                        }
                    } catch (error) {
                        this.$message.error('代码预览失败');
                        console.error('Error:', error);
                    } finally {
                        this.previewing = false;
                    }
                },
                selectFile(tableName, fileName, content) {
                    this.currentFile = tableName ? tableName + '_' + fileName : fileName;
                    this.currentFileName = fileName;
                    this.currentFileContent = content;
                    // 确保代码内容更新后再应用高亮
                    this.$nextTick(() => {
                        const codeBlock = document.querySelector('pre code');
                        if (codeBlock) {
                            codeBlock.textContent = content;
                            hljs.highlightBlock(codeBlock);
                        }
                    });
                },
                downloadCurrentFile() {
                    const blob = new Blob([this.currentFileContent], { type: 'text/plain' });
                    const link = document.createElement('a');
                    link.href = window.URL.createObjectURL(blob);
                    link.download = this.currentFileName;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    window.URL.revokeObjectURL(link.href);
                },
                downloadAllFiles() {
                    if (this.isMultiSelect) {
                        this.generateSelectedTables();
                    } else {
                        this.generateCode();
                    }
                    this.previewVisible = false;
                }
            }
        });
    </script>
</body>
</html>
